package com.bclz.errorhandling

import scala.annotation.tailrec
import scala.collection.immutable.Nil

/**
  * 对函数 map getOrElse flatMap orElse filter
  * 对函数 mean variance map2 Try sequence traverse实现
  * For的推导
  *
  */
sealed trait Option[+A] {

  //如果Option不为None.对其应用f
  def map[B](f: A => B): Option[B] = this match {
    case None => None
    case Some(v) => Some(f(v))
  }

  //default: =>B表示不是立即求值(非严格求值)，而是函数需要时才求值
  def getOrElse[B >: A](default: => B): B = this match {
    case None => default
    case Some(v) => v
  }

  //如果Option不为None.对其应用f,可能会失败
  def flatMap[B](f: A => Option[B]): Option[B] = this match {
    case None => None
    case Some(v) => f(v)
  }

  def flatMap2[B](f: A => Option[B]): Option[B] =
    map(f) getOrElse None

  //不对ob求值，除非需要(非严格求值)
  def orElse[B >: A](ob: => Option[B]): Option[B] = this match {

    case None => ob
    case Some(v) => Some(v)

  }

  def orElse2[B >: A](ob: => Option[B]): Option[B] =
    map(Some(_)) getOrElse ob

  //如果值不满足f，则返回None
  def filter(f: A => Boolean): Option[A] = this match {
    case Some(v) if (f(v)) => Some(v)
    case _ => None
  }

  def filter2(f: A => Boolean): Option[A] =
    flatMap2(a => if (f(a)) Some(a) else None)


}

object Option {


  def mean(xs: Seq[Double]): Option[Double] = {

    if (xs.isEmpty) None
    else Some(xs.sum / xs.size)

  }

  def variance(xs: Seq[Double]): Option[Double] =
    mean(xs) map (a => xs.reduce[Double]((a1, a2) => math.pow(a1 - a, 2) + math.pow(a2 - a, 2)) / xs.size)

//  def map2[A, B, C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = (a, b) match {
//    case (_, None) => None
//    case (None, _) => None
//    case (Some(v1), Some(v2)) => Some(f(v1, v2))
//  }

  //两个参数的函数提升，适用于两个option应用(A,B)=>C函数
  def map2[A, B, C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] =
    a flatMap(av=> b.map(f(av,_)))

  //异常处理
  //  def Try[A,B](f:A=>B):Option[B]={
  def Try[A](a: => A): Option[A] = { //惰性求值
    try Some(a)
    catch {
      case _: Exception => None //这样会丢失错误信息
    }

  }

  def sequence[A](a:List[Option[A]]):Option[List[A]]={
    @tailrec
    def recursion(resource:List[Option[A]],newList:List[A]): Option[List[A]] = resource match {

      case Nil => Some(newList)
      case None::_ => None
      case Some(v)::t=>recursion(t,v::Nil:::newList)
    }
    recursion(a,Nil)

  }
  //flatMap实现sequence,无伪递归优化，容易栈溢出
  def sequence1[A](a:List[Option[A]]):Option[List[A]]=a match {
    case Nil => Some(Nil)
    case aa::bb=>aa.flatMap(aaa=>sequence1(bb) map(aaa::_) )
  }

  //foldRight实现sequence,效率不高，为None时不能立即返回
  def sequence2[A](a:List[Option[A]]):Option[List[A]]=
    a.foldRight[Option[List[A]]](Some(Nil))((aa,bb)=> map2(aa,bb)(_::_))

  def traverse[A,B](l:List[A])(f:A=>Option[B]):Option[List[B]]= l match {

    case Nil=>Some(Nil)
//    case h::t=>f(h) flatMap(b=> traverse(t)(f).map(b::_) )
    case h::t=>map2(f(h),traverse(t)(f))(_::_)
  }

  def traverse2[A,B](l:List[A])(f:A=>Option[B]):Option[List[B]]=
    l.foldRight[Option[List[B]]](Some(Nil))((a,b)=>map2(f(a),b)(_::_))

  def sequenceViaTraverse[A](a:List[Option[A]]):Option[List[A]]=
    traverse(a)(aa=>aa)

  def map2ViaForComprehension[A,B,C](a:Option[A],b:Option[B])(f:(A,B)=>C):Option[C]=
    for{
      aa <- a
      bb <- b
    }yield f(aa,bb)


}

case class Some[+A](v: A) extends Option[A]

case object None extends Option[Nothing]

